package mt.client;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import mt.Order;
import mt.comm.ClientComm;
import mt.comm.ClientSideMessage;
import mt.dummy.ClientCommDummy;

public class GUI extends JFrame implements MicroTraderClient {

	private static final long serialVersionUID = 1L;
	/**
	 * This class contains three panel that has all the functionality of a client
	 * @author group 55
	 */
	/**
	 * Is constant and sets the size of the window
	 */
	private final Dimension FRAME_SIZE = new Dimension(500, 500);
	/**
	 *  responsible for all the functions related to connection
	 */
	private TopPanel topPanel;
	/**
	 * responsible for all the functions related to order
	 */
	private MiddlePanel middlePanel;
	/**
	 *  responsible to provide the customer with the last order
	 * received by the server.
	 */
	private EndPanel endPanel;
	/**
	 * Orders Window
	 */
	private ViewOrderWindow ordersViewer;
	/**
	 * Nickname of the client
	 */
	private String nickname;
	/**
	 * responsible for communication between client server
	 */
	private ClientCommDummy cliComm;
	/**
	 * responsible for keeping all the information you the order sent by the server
	 */
	private final HashMap<Integer,Order> orders = new HashMap<Integer,Order>();
	/**
	 * Name of the host
	 */
	private final String server;

	/**
	 * Nickname of the client
	 * @param nickname     Nickname of the client
	 * @param topPanel     responsible for all the functions related to connection
	 * @param middlePannel responsible for all the functions related to order
	 * @param endPanel     responsible to provide the customer with the last order
	 *                 received by the server.
	 */
	public GUI(String server) {
		this.server = server;
		setLookNFeel();
		setNickname(JOptionPane.showInputDialog(this, "Nickname: "));
	}

	public void connect() {
		if(nickname != null){
			cliComm = new ClientCommDummy();
			cliComm.connect(server, getNickname());
			if(cliComm.isConnected()){
				init();
				start(cliComm);
				topPanel.getConnectButton().setEnabled(false);
				topPanel.getDisconnectButton().setEnabled(true);
			}
			else
				this.dispose();
		}
	}

	/**
	 * Procedure responsible for the initiation of the customer,
	 * ie start graphics and connection to the server
	 */

	public void init() {
		topPanel = new TopPanel(nickname);
		middlePanel = new MiddlePanel();
		endPanel = new EndPanel();
		build();
		connectionListener();
		movimentListener();
		ordersListener();
		setVisible(true);
	}

	/**
	 * procedure responsible for the layout configuration
	 */

	private void build() {
		setTitle("MicroTrader");
		setSize(FRAME_SIZE);
		setResizable(false);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		setLayout(new GridBagLayout());
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent ev) {
				disconnect();
				ordersViewer.dispose();
				dispose();
			}
		});

		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.BOTH;
		gbc.weightx = 1;

		// topPanel
		gbc.weighty = 0.1;
		gbc.gridy = 0;
		add(topPanel, gbc);

		// middlePanel
		gbc.weighty = 0.9;
		gbc.gridy = 1;
		add(middlePanel, gbc);

		// endPanel
		gbc.gridy = 2;
		add(endPanel, gbc);
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}

	/**
	 * Procedure responsible for carry out a connection to the server
	 */
	@Override
	public void start(ClientComm clientComm) {
		new Thread(new Runnable() {

			@Override
			public void run() {

				setTradeButtonsOn();
				while (getComm().isConnected()) {
					if (getComm().hasNextMessage()) {
						ClientSideMessage message = getComm().getNextMessage();
						Order order = message.getOrder();
						System.out.println("<" + getNickname() + "> Message Received: " + order );
						if (message.getType().equals(mt.comm.ClientSideMessage.Type.ORDER)) {
							if (!orders.containsKey(order.getServerOrderID())) {
								orders.put(order.getServerOrderID(), order);
							} else {
								updateOrder(order);
							}
						} else {
							System.out.println("ERROR: " + message.getError());
						}
					}
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
	}

	private void setTradeButtonsOn() {
		middlePanel.getBuy().setEnabled(true);
		middlePanel.getSell().setEnabled(true);
	}

	private void setTradeButtonsOff() {
		middlePanel.getBuy().setEnabled(false);
		middlePanel.getSell().setEnabled(false);
	}


	/**
	 * Procedure responsible for updating an order
	 * @param order
	 */

	private void updateOrder(Order newOrder) {
		System.out.println("Order changed: " + newOrder);

		if(newOrder.getNumberOfUnits() == 0)
			orders.remove(newOrder.getServerOrderID());
		else{
			orders.replace(newOrder.getServerOrderID(), newOrder);
		}
	}

	/**
	 * Procedures responsible for the interaction of buttons related to a connection server
	 */
	private void connectionListener(){
		topPanel.getConnectButton().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				connect();
			}
		});

		topPanel.getDisconnectButton().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				disconnect();
			}
		});
	}

	public void disconnect() {
		topPanel.getConnectButton().setEnabled(true);
		getComm().disconnect();
		topPanel.getDisconnectButton().setEnabled(false);
		setTradeButtonsOff();
		orders.clear();
	}

	public HashMap<Integer, Order> getOrders() {
		return orders;
	}

	/**
	 * Procedures responsible for the interaction of buttons related to buy and sell orders
	 */
	private void ordersListener() {
		middlePanel.getBuy().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				String stock = JOptionPane.showInputDialog(GUI.this, "Insert the name of the Stock:", "MicroTrader", JOptionPane.QUESTION_MESSAGE);
				String numUnits = JOptionPane.showInputDialog(GUI.this,"Insert the number of units you want to buy:","MicroTrader", JOptionPane.QUESTION_MESSAGE);
				String price = JOptionPane.showInputDialog(GUI.this,"Insert the price of each units you want to buy:","MicroTrader", JOptionPane.QUESTION_MESSAGE);
				int numberOfUnits;
				double pricePerUnit;
				try{
					numberOfUnits = Integer.parseInt(numUnits);
					pricePerUnit = Double.parseDouble(price);
					Order order = new Order(nickname, true, stock, numberOfUnits, pricePerUnit);
					getComm().sendOrder(order);
				}catch(Exception ex){
					JOptionPane.showMessageDialog(GUI.this, "Invalid Input!\nOrder has been canceled.", "MicroTrader", JOptionPane.ERROR_MESSAGE);
				}
			}
		});

		middlePanel.getSell().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				String stock = JOptionPane.showInputDialog(GUI.this, "Insert the name of the Stock:","MicroTrader", JOptionPane.QUESTION_MESSAGE);
				String numUnits = JOptionPane.showInputDialog(GUI.this, "Insert the number of units you want to sell:","MicroTrader", JOptionPane.QUESTION_MESSAGE);
				String price = JOptionPane.showInputDialog(GUI.this, "Insert the price of each units you want to sell:","MicroTrader", JOptionPane.QUESTION_MESSAGE);
				int numberOfUnits;
				double pricePerUnit;
				try{
					numberOfUnits = Integer.parseInt(numUnits);
					pricePerUnit = Double.parseDouble(price);
					Order order = new Order(nickname, false, stock, numberOfUnits, pricePerUnit);
					getComm().sendOrder(order);
				}catch(Exception ex){
					JOptionPane.showMessageDialog(GUI.this, "Invalid Input!\nOrder has been canceled.", "MicroTrader", JOptionPane.ERROR_MESSAGE);
				}
			}
		});
	}

	/**
	 * Procedures responsible for the interaction of button to view available orders
	 */
	private void movimentListener() {
		middlePanel.getViewOrders().addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				ordersViewer = new ViewOrderWindow(orders);
			}
		});
	}

	public ClientCommDummy getComm() {
		return cliComm;
	}

	/**
	 * returns the client nickname
	 */
	public String getNickname() {
		return nickname;
	}

	/**
	 * Set Look & Feel
	 */
	private void setLookNFeel(){
		try {

			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		}
		catch (UnsupportedLookAndFeelException e) {}
		catch (ClassNotFoundException e) {}
		catch (InstantiationException e) {}
		catch (IllegalAccessException e) {}
	}
}
